#include "DBRecord.h"
#include "DBConfig.h"
#include "DataBase.h"
#include <algorithm>

// ------------------------------------- Config ------------------------------------------

namespace db
{

REGISTER_CONTAINER_CREATOR(NONE, return NULL );
REGISTER_CONTAINER_CREATOR(STRING, return new StringContainer() );
REGISTER_CONTAINER_CREATOR(DATA, return new DataContainer() );

static Container* CONTAINER_CREATOR_INT ()
{
    return new SimpleContainer<int, RTP_INT>();
}

static Container* CONTAINER_CREATOR_BOOL ()
{
    return new SimpleContainer<bool, RTP_BOOL>();
}

static Container* CONTAINER_CREATOR_FLOAT ()
{
    return new SimpleContainer<float, RTP_FLOAT>();
}

static Container* CONTAINER_CREATOR_DOUBLE ()
{
    return new SimpleContainer<double, RTP_DOUBLE>();
}

static const CONTAINER_CREATOR RecordTypesCreators[RTP_COUNT] = {
    GET_CONTAINER_CREATOR(NONE),          // RT_NONE
    GET_CONTAINER_CREATOR(STRING),        // RT_STRING
    GET_CONTAINER_CREATOR(BOOL),          // RT_BOOL
    GET_CONTAINER_CREATOR(INT),           // RT_INT
    GET_CONTAINER_CREATOR(FLOAT),         // RT_FLOAT
    GET_CONTAINER_CREATOR(DOUBLE),        // RT_DOUBLE
    GET_CONTAINER_CREATOR(DATA),          // RT_DATA
};

// ----------------------------------- Record ----------------------------------------

void Record::Set(const bool data) { Set<bool, RTP_BOOL>(data); }
void Record::Set(const u32 data) { Set<u32, RTP_INT>(data); }
void Record::Set(const f32 data) { Set<f32, RTP_FLOAT>(data); }
void Record::Set(const f64 data) { Set<f64, RTP_DOUBLE>(data); }

std::wstring Record::GetAsString() const { CHECK_CONTAINER_AND_RETURN(GetAsString, L"") }
bool Record::GetAsBool() const { CHECK_CONTAINER_AND_RETURN(GetAsBool, false) }
u32 Record::GetAsInt() const { CHECK_CONTAINER_AND_RETURN(GetAsInt, 0) }
f32 Record::GetAsFloat() const { CHECK_CONTAINER_AND_RETURN(GetAsFloat, 0.f) }
f64 Record::GetAsDouble() const { CHECK_CONTAINER_AND_RETURN(GetAsDouble, 0.f) }

u32 Record::GetDataSize() const { CHECK_CONTAINER_AND_RETURN(GetDataSize, 0) }
const void* Record::GetAsData() const { CHECK_CONTAINER_AND_RETURN(GetAsData, NULL) }

void Record::setClass(const ERecordClass recordClass)
{
    if (m_child_array)
    {
        for (SizeType i = 0, t = m_child_array->size(); i < t; ++i)
            delete (*m_child_array)[i];
        delete m_child_array;
        m_child_array = NULL;
    }
    if (m_child_map)
    {
        for (std::map<std::wstring, Record*>::iterator it = m_child_map->begin(); it != m_child_map->end(); ++it)
        {
            delete it->second;
        }
        delete m_child_map;
        m_child_map = NULL;
    }

	m_class = recordClass;

	switch (m_class)
	{
		case RC_NAMEDCHILD:
		{
			m_child_map = new std::map<std::wstring, Record*>();
			break;
		}
		case RC_ARRAY:
		{
			m_child_array = new std::deque<Record*>();
			break;
		}
	}
}

const u32 Record::getSize() const
{
	switch (m_class)
	{
		case RC_NAMEDCHILD:
		{
			return m_child_map->size();
		}
		case RC_ARRAY:
		{
			return m_child_array->size();
		}
	}
	return 0;
}


const std::wstring* Record::getName() const 
{ 
	return m_name; 
}

void Record::setName(const std::wstring& name) 
{ 
	if (m_name)
	{
		*m_name = name; 
	}
	else 
	{
		m_name = new std::wstring(name); 
	}
}

void Record::setSize(const u32 newSize)
{
	if (m_class != RC_ARRAY)
	{
        setClass(RC_ARRAY);
    }
	m_child_array->resize(newSize, NULL);
}


const Record& Record::operator[](const std::wstring &name) const
{
    if (m_class == RC_NAMEDCHILD)
    {
	    std::map<std::wstring, Record*>::const_iterator id = m_child_map->find(name);
	    if (id == m_child_map->end()) 
        {
            return (*this);
        }
	    return *(id->second);
    }
    return (*this);
}

const Record& Record::operator[](const u32 index) const
{
    if (m_class == RC_ARRAY)
    {
	    if (index >= getSize()) 
        {
            return (*this);
        }
        if (!(*m_child_array)[index])
        {
            return (*this);
        }
	    return *(*m_child_array)[index];
    }
    return (*this);
}

bool Record::remove(const std::wstring &name, bool freeAfter /* = true */)
{
    if (m_class == RC_NAMEDCHILD)
    {
	    std::map<std::wstring, Record*>::iterator id = m_child_map->find(name);
	    if (id != m_child_map->end()) 
        {
            if (freeAfter)
            {
                delete id->second;
            }
            m_child_map->erase(id);
        }
    }
    return false;
}

bool Record::remove(const u32 index, bool freeAfter /* = true */)
{
    if (m_class == RC_ARRAY)
    {
	    if (index >= getSize()) 
        {
            if (freeAfter) delete (*m_child_array)[index];
            m_child_array->erase(m_child_array->begin() + index);
            return true;
        }
    }
    return false;
}


Record& Record::operator[](const std::wstring &name)
{
    switch (m_class)
    {
        case RC_ARRAY:
        case RC_NOCHILD:
        {
            setClass(RC_NAMEDCHILD);
            break;
        }
    }
	std::map<std::wstring, Record*>::iterator id = m_child_map->find(name);
	if (id == m_child_map->end()) 
    {
        Record *newRecord = new Record(RC_NOCHILD);
		newRecord->setName(name);
        (*m_child_map)[name] = newRecord;
        return *newRecord;
    }
	return *(id->second);
}

Record& Record::operator[](const u32 index)
{
    switch (m_class)
    {
        case RC_NAMEDCHILD:
        case RC_NOCHILD:
        {
            setClass(RC_ARRAY);
            break;
        }
    }
	if (index >= getSize()) 
    {
        setSize(index + 1);
    }
    if (!(*m_child_array)[index])
    {
        (*m_child_array)[index] = new Record();
    }
	return *(*m_child_array)[index];
}


Record::iterator Record::childBegin()
{
    if (m_class == RC_NAMEDCHILD)
    {
        if (m_child_map)
        {
            return m_child_map->begin();
        }
    }
    return std::map<std::wstring, Record*>::iterator();
}

Record::iterator Record::childEnd()
{
    if (m_class == RC_NAMEDCHILD)
    {
        if (m_child_map)
        {
            return m_child_map->end();
        }
    }
    return std::map<std::wstring, Record*>::iterator();
}

Record::const_iterator Record::childBegin() const 
{
    if (m_class == RC_NAMEDCHILD)
    {
        if (m_child_map)
        {
            return m_child_map->begin();
        }
    }
    return std::map<std::wstring, Record*>::const_iterator();
}

Record::const_iterator Record::childEnd() const
{
    if (m_class == RC_NAMEDCHILD)
    {
        if (m_child_map)
        {
            return m_child_map->end();
        }
    }
    return std::map<std::wstring, Record*>::const_iterator();
}

void Record::Clear()
{
	DELETE_AND_NULL(m_name);
    setClass(RC_NOCHILD);
    setType(RTP_NONE, NULL);
}


Record::Record(ERecordClass recordClass) :
    m_type(RTP_NONE),
    m_container(NULL),
	m_child_array(NULL),
	m_child_map(NULL),
	m_name(NULL)
{
    setClass(recordClass);
}    
    
Record::Record(Container* container, ERecordClass recordClass) :
    m_type(RTP_NONE),
    m_container(NULL),
	m_child_array(NULL),
	m_child_map(NULL),
    m_name(NULL)
{
    if (container)
    {
        setType(container->GetType());
        setContainer(container);
    }
    setClass(recordClass);
}

Record::~Record()
{
    Clear();
}


void Record::Load(ReadStream& file)
{
    RecordFlags flags;
    file.read(&flags, sizeof(flags));

    if ((flags & RF_HAS_NAME) > 0)
    {
		std::wstring temp;
        FileHelper::read_wstring(file, temp);
		setName(temp);
    }

    if ((flags & RF_HAS_DATA) > 0)
    {
        ERecordType type;
        file.read(&type, sizeof(type));
        setType(type);
        Container *cnt = RecordTypesCreators[type]();
        setContainer(cnt);
        if (cnt)
        {
            cnt->Load(file);
        }
    }
    else
    {
        setType(RTP_NONE);
    }

    if ((flags & RF_HAS_CHILD) > 0)
    {
        setClass( (flags & RF_ARRAY) > 0 ? RC_ARRAY : RC_NAMEDCHILD );
        
        SizeType sz;
        file.read(&sz, sizeof(sz));

        if (sz > 0)
        {
            switch (m_class)
            {
                case RC_ARRAY:
                {
                    setSize(sz);
                    break;
                }
            }
            for (SizeType i = 0; i < sz; ++i)
            {
                switch (m_class)
                {
                    case RC_NAMEDCHILD:
                    {
                        Record *chld = new Record();
                        chld->Load(file);
						const std::wstring *str = chld->getName();
						if (str)
							(*m_child_map)[*str] = chld;
                        break;
                    }
                    case RC_ARRAY:
                    {
                        Record *chld = new Record();
                        chld->Load(file);
                        (*m_child_array)[i] = chld;
                        break;
                    }
                }
            }
        }
    }
}

void Record::Save(WriteStream& file)
{
    RecordFlags flags = 
        RF_HAS_NAME     * (int)(m_name)					 |
        RF_HAS_DATA     * (int)(getType() != RTP_NONE)   |
        RF_HAS_CHILD    * (int)(getSize() > 0)           |
        RF_ARRAY        * (int)(m_class == RC_ARRAY);

    file.write(&flags, sizeof(flags));

    if (m_name)
    {
        FileHelper::write_wstring(file, *m_name);
    }
    if (getType() != RTP_NONE)
    {
        ERecordType type = getType();
        file.write(&type, sizeof(type));
        const Container *cnt = getContainer();
        if (cnt)
        {
            cnt->Save(file);
        }
    }
    if (getSize() > 0)
    {
        const SizeType sz = getSize();
        file.write(&sz, sizeof(sz));
        switch (m_class)
        {
            case RC_NAMEDCHILD:
            {
                for (std::map<std::wstring, Record*>::iterator it = m_child_map->begin(); it != m_child_map->end(); ++it)
                {
                    it->second->Save(file);
                }
                break;
            }
            case RC_ARRAY:
            {
                for (SizeType i = 0; i < sz; ++i)
                {
                    Record &chld = (*this)[i];
                    chld.Save(file);
                }
                break;
            }
        }
    }
}

void Record::setType(const ERecordType type, Container *cnt)
{
    setType(type);
    if (m_container) delete m_container;
    m_container = cnt;
}
    
void Record::Set(const wchar_t *data)
{
    Set(std::wstring(data));
}

void Record::Set(const std::wstring& data)
{
    if (checkType(RTP_STRING))
    {
        StringContainer *cnt = dynamic_cast<StringContainer*>(m_container);
        if (cnt)
        {
           cnt->Set(data);
           return;
        }
    }
    setType(RTP_STRING, new StringContainer(data));
}

void Record::Set(const void *data, u32 size)
{
    if (checkType(RTP_DATA))
    {
        DataContainer *cnt = dynamic_cast<DataContainer*>(m_container);
        if (cnt)
        {
           cnt->Set(data, size);
           return;
        }
    }
    setType(RTP_DATA, new DataContainer(data, size));
}

};